﻿#region License
// MIT License
//
// Copyright(c) 2019-2025 ZhangYu
// https://gitee.com/zhangyu800/litebyte
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#endregion
#region Intro
// Purpose: Reflection Optimize Performance
// Author: ZhangYu
// CreateDate: 2022-05-01
// LastModifiedDate: 2022-05-28
#endregion
// 使用IL优化反射
//#define USE_IL_REFLECTION
#if USE_IL_REFLECTION
namespace LiteByte.Reflection {

    using System;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Collections.Generic;

    /// <summary> IL.Emit工具 ZhangYu 2022-05-01 </summary>
    public static class ILUtil {

        private static Type[] NoTypes = new Type[] { };

        /// <summary> IL动态添加创建新对象方法 </summary>
        public static Func<object> CreateCreateInstance(Type classType) {
            DynamicMethod method = new DynamicMethod(string.Empty, typeof(object), null, classType);
            if (classType.IsValueType) {
                // 实例化值类型
                ILGenerator il = method.GetILGenerator(32);
                var v0 = il.DeclareLocal(classType);
                il.Emit(OpCodes.Ldloca_S, v0);
                il.Emit(OpCodes.Initobj, classType);
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Box, classType);
                il.Emit(OpCodes.Ret);
            } else {
                // 实例化引用类型
                ConstructorInfo ctor = classType.GetConstructor(NoTypes);
                ILGenerator il = method.GetILGenerator(16);
                il.Emit(OpCodes.Newobj, ctor);
                il.Emit(OpCodes.Ret);
            }
            return method.CreateDelegate(typeof(Func<object>)) as Func<object>;
        }

        /// <summary> IL动态添加创建新数组方法 </summary>
        public static Func<int, Array> CreateCreateArray(Type elementType, int length) {
            DynamicMethod method = new DynamicMethod(string.Empty, typeof(Array), new Type[] { typeof(int) }, typeof(Array));
            ILGenerator il = method.GetILGenerator(16);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Newarr, elementType);
            il.Emit(OpCodes.Ret);
            return method.CreateDelegate(typeof(Func<int, Array>)) as Func<int, Array>;
        }

        private static Dictionary<Type, Func<object>> createInstanceCache = new Dictionary<Type, Func<object>>();
        private static Dictionary<Type, Func<int, Array>> createArrayCache = new Dictionary<Type, Func<int, Array>>();

        /// <summary> IL动态创建新的实例 </summary>
        /// <param name="classType">类型</param>
        public static object CreateInstance(Type classType) {
            Func<object> createMethod = null;
            if (!createInstanceCache.TryGetValue(classType, out createMethod)) {
                createMethod = CreateCreateInstance(classType);
                createInstanceCache.Add(classType, createMethod);
            }
            return createMethod();
        }


        /// <summary> IL动态创建新数组 </summary>
        public static Array CreateArray(Type classType, int length) {
            Func<int, Array> createMethod = null;
            if (!createArrayCache.TryGetValue(classType, out createMethod)) {
                createMethod = CreateCreateArray(classType, length);
                createArrayCache.Add(classType, createMethod);
            }
            return createMethod(length);
        }

        /// <summary> IL.Emit动态创建获取字段值的方法 </summary>
        private static Func<object, object> CreateGetField(Type classType, string fieldName) {
            FieldInfo field = classType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public);
            DynamicMethod method = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, classType);
            ILGenerator il = method.GetILGenerator(16); // 默认大小64字节
            il.Emit(OpCodes.Ldarg_0);
            if (classType.IsValueType) il.Emit(OpCodes.Unbox_Any, classType);
            il.Emit(OpCodes.Ldfld, field);
            if (field.FieldType.IsValueType) il.Emit(OpCodes.Box, field.FieldType);
            il.Emit(OpCodes.Ret);
            return method.CreateDelegate(typeof(Func<object, object>)) as Func<object, object>;
        }

        /// <summary> IL.Emit动态创建设置字段方法 </summary>
        private static Action<object, object> CreateSetField(Type classType, string fieldName) {
            FieldInfo field = classType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public);
            DynamicMethod method = new DynamicMethod(string.Empty, null, new Type[] { typeof(object), typeof(object) }, classType);
            ILGenerator il = method.GetILGenerator(32); // 默认大小64字节
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(classType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, classType);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(field.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.FieldType);
            il.Emit(OpCodes.Stfld, field);
            il.Emit(OpCodes.Ret);
            return method.CreateDelegate(typeof(Action<object, object>)) as Action<object, object>;
        }

        /// <summary> IL.Emit动态创建获取属性值的方法 </summary>
        private static Func<object, object> CreateGetProperty(Type classType, string propertyName) {
            PropertyInfo property = classType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
            DynamicMethod method = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, classType);
            ILGenerator il = method.GetILGenerator(32);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(classType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, classType);
            il.Emit(OpCodes.Call, property.GetGetMethod());
            if (property.PropertyType.IsValueType) il.Emit(OpCodes.Box, property.PropertyType);
            il.Emit(OpCodes.Ret);
            return method.CreateDelegate(typeof(Func<object, object>)) as Func<object, object>;
        }

        /// <summary> IL.Emit动态创建设置属性值的方法 </summary>
        private static Action<object, object> CreateSetProperty(Type classType, string propertyName) {
            PropertyInfo property = classType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
            MethodInfo methodInfo = property.GetSetMethod();
            ParameterInfo parameter = methodInfo.GetParameters()[0];
            DynamicMethod method = new DynamicMethod(string.Empty, null, new Type[] { typeof(object), typeof(object) }, classType);
            ILGenerator il = method.GetILGenerator(32);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(classType.IsValueType ? OpCodes.Unbox : OpCodes.Castclass, classType);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(parameter.ParameterType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, parameter.ParameterType);
            il.Emit(OpCodes.Call, methodInfo);
            il.Emit(OpCodes.Ret);
            return method.CreateDelegate(typeof(Action<object, object>)) as Action<object, object>;
        }

        /// <summary> IL.Emit动态创建获取字段或属性值的方法 </summary>
        /// <param name="classType">对象类型</param>
        /// <param name="fieldName">字段(或属性)名称</param>
        public static Func<object, object> CreateGetValue(Type classType, string fieldName) {
            MemberInfo[] members = classType.GetMember(fieldName, BindingFlags.Instance | BindingFlags.Public);
            if (members.Length == 0) {
                string error = "Type [{0}] don't contains member [{1}]";
                throw new Exception(string.Format(error, classType, fieldName));
            }
            Func<object, object> getValue = null;
            switch (members[0].MemberType) {
                case MemberTypes.Field:
                    getValue = CreateGetField(classType, fieldName);
                    break;
                case MemberTypes.Property:
                    getValue = CreateGetProperty(classType, fieldName);
                    break;
                default:
                    break;
            }
            return getValue;
        }

        /// <summary> IL.Emit动态创建设置字段值(或属性)值的方法 </summary>
        /// <param name="classType">对象类型</param>
        /// <param name="fieldName">字段(或属性)名称</param>
        public static Action<object, object> CreateSetValue(Type classType, string fieldName) {
            MemberInfo[] members = classType.GetMember(fieldName, BindingFlags.Instance | BindingFlags.Public);
            if (members.Length == 0) {
                string error = "Type [{0}] does not contain field [{1}]";
                throw new Exception(string.Format(error, classType, fieldName));
            }
            Action<object, object> setValue = null;
            switch (members[0].MemberType) {
                case MemberTypes.Field:
                    setValue = CreateSetField(classType, fieldName);
                    break;
                case MemberTypes.Property:
                    setValue = CreateSetProperty(classType, fieldName);
                    break;
                default:
                    break;
            }
            return setValue;
        }

        // Emit Getter 方法缓存字典
        private static Dictionary<Type, Dictionary<string, Func<object, object>>> getValueCache = new Dictionary<Type, Dictionary<string, Func<object, object>>>();
        // Emit Setter 方法缓存字典
        private static Dictionary<Type, Dictionary<string, Action<object, object>>> setValueCache = new Dictionary<Type, Dictionary<string, Action<object, object>>>();

        /// <summary> IL 获取对象成员的值(字段或属性) </summary>
        public static object GetValue(object obj, string fieldName) {
            // 查找一级缓存
            Type classType = obj.GetType();
            Dictionary<string, Func<object, object>> cache = null;
            if (!getValueCache.TryGetValue(classType, out cache)) {
                cache = new Dictionary<string, Func<object, object>>();
                getValueCache.Add(classType, cache);
            }

            // 查找二级缓存
            Func<object, object> getValue = null;
            if (!cache.TryGetValue(fieldName, out getValue)) {
                getValue = CreateGetValue(classType, fieldName);
                cache.Add(fieldName, getValue);
            }
            return getValue(obj);
        }

        /// <summary> IL 设置对象成员的值(字段或属性) </summary>
        public static void SetValue(object obj, string fieldName, object value) {
            // 查找一级缓存
            Type classType = obj.GetType();
            Dictionary<string, Action<object, object>> cache = null;
            if (!setValueCache.TryGetValue(classType, out cache)) {
                cache = new Dictionary<string, Action<object, object>>();
                setValueCache.Add(classType, cache);
            }

            // 查找二级缓存
            Action<object, object> setValue = null;
            if (!cache.TryGetValue(fieldName, out setValue)) {
                setValue = CreateSetValue(classType, fieldName);
                cache.Add(fieldName, setValue);
            }
            setValue(obj, value);
        }

        /// <summary> 清理已缓存IL方法 </summary>
        public static void ClearCache() {
            createInstanceCache.Clear();
            createArrayCache.Clear();
            getValueCache.Clear();
            setValueCache.Clear();
        }

    }

}
#endif